﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static light.http.server.httpserver;

namespace server_anychat
{
    static class ChatServer
    {
        static LockObj lockidobj = new LockObj();
        static UInt64 _lastestMsgID;
        public static UInt64 lastestMsgID
        {
            get
            {
                lock (lockidobj)
                {
                    return _lastestMsgID;
                }
            }
            private set
            {
                lock (lockidobj)
                {
                    _lastestMsgID = value;
                }
            }
        }
        public static void Init()
        {
            allpeers = new System.Collections.Concurrent.ConcurrentDictionary<Guid, ChatPeer>();

            lastestMsgID = 0;
            if (System.IO.Directory.Exists("data") == false)
            {
                System.IO.Directory.CreateDirectory("data");
            }
            else
            {
                var files = System.IO.Directory.GetFiles("data", "msg_*.txt");
                foreach (var f in files)
                {
                    var id = System.IO.Path.GetFileNameWithoutExtension(f).Replace("msg_", "");
                    var idm = UInt64.Parse(id, System.Globalization.NumberStyles.HexNumber);
                    if (lastestMsgID < idm)
                    {
                        lastestMsgID = idm;
                    }
                }
            }
        }
        public static light.http.server.httpserver.IWebSocketPeer OnWebSocketConnect(System.Net.WebSockets.WebSocket websocket)
        {
            return new ChatPeer(websocket);
        }
        public static void OnConnect(ChatPeer peer)
        {
            allpeers.TryAdd(peer.guid, peer);
        }
        //一个玩家断线，群发
        public static void OnDisConnect(ChatPeer peer)
        {
            if (allpeers.TryRemove(peer.guid, out ChatPeer _p))
            {
                try
                {
                    foreach (var a in allpeers)
                    {
                        if (a.Key == peer.guid)
                            continue;
                        a.Value.Send_UserExit(peer.guid);
                    }
                }
                catch
                {
                }
            }
        }
        static System.Collections.Concurrent.ConcurrentDictionary<Guid, ChatPeer> allpeers;

        //一个玩家改名，群发
        public static void OnSetName(ChatPeer peer)
        {
            try
            {
                foreach (var a in allpeers)
                {
                    a.Value.Send_UserIn(peer.guid, peer.name, a.Key == peer.guid);
                }
            }
            catch
            {
            }
        }
        public static void OnSendMsg(string msg)
        {
            try
            {
                lastestMsgID++;
                SaveMsg(lastestMsgID, msg);
                //一个玩家发了条消息，也群发
                foreach (var a in allpeers)
                {
                    a.Value.Send_Msg(lastestMsgID, msg);
                }
            }
            catch
            {

            }
        }
        class LockObj
        {

        }
        static LockObj lockobj = new LockObj();
        public static void SaveMsg(UInt64 id, string msg)
        {
            var filename = "data" + System.IO.Path.DirectorySeparatorChar + "msg_" + id.ToString("X16") + ".txt";
            try
            {
                lock (lockobj)
                {
                    System.IO.File.WriteAllText(filename, msg);
                }
            }
            catch
            {

            }
        }
        public static string GetMsg(UInt64 id)
        {
            var filename = "data" + System.IO.Path.DirectorySeparatorChar + "msg_" + id.ToString("X16") + ".txt";

            try
            {
                lock (lockobj)
                {
                    var msg = System.IO.File.ReadAllText(filename);
                    return msg;
                }
            }
            catch
            {
                return null;
            }
        }
        public static Dictionary<Guid, string> GetAllUser()
        {
            Dictionary<Guid, string> result = new Dictionary<Guid, string>();

            foreach (var a in allpeers)
            {
                try
                {
                    result[a.Key] = a.Value.name;
                }
                catch
                {

                }
            }
            return result;
        }
    }
    class ChatPeer : IWebSocketPeer
    {
        System.Net.WebSockets.WebSocket websocket;
        public string name
        {
            get;
            private set;
        }
        public System.Guid guid
        {
            get;
            private set;
        }
        public ChatPeer(System.Net.WebSockets.WebSocket websocket)
        {
            this.websocket = websocket;
            this.guid = System.Guid.NewGuid();
            this.name = "noname:" + guid;
        }
        public async Task OnConnect()
        {
            ChatServer.OnConnect(this);
        }

        public async Task OnDisConnect()
        {
            ChatServer.OnDisConnect(this);
        }

        public async Task OnRecv(MemoryStream stream, int count)
        {
            byte[] buf = new byte[count];
            stream.Read(buf, 0, buf.Length);
            var msg = System.Text.Encoding.UTF8.GetString(buf);
            JObject jobj = JObject.Parse(msg);
            if (jobj.ContainsKey("cmd"))
            {
                var cmd = jobj["cmd"].ToString();
                if (cmd == "setname")
                {
                    Recv_SetName(jobj["name"].ToString());
                }
                else if (cmd == "sendmsg")
                {
                    Recv_SendMsg(jobj["msg"].ToString());
                }
                else if (cmd == "getallusr")
                {
                    Recv_GetAllUser();
                }
                else if (cmd == "getmsgid")
                {
                    Recv_GetLastestMagID();
                }
                else if (cmd == "getmsg")
                {
                    var id = UInt64.Parse(jobj["msgid"].ToString());
                    Recv_GetMsg(id);
                }
            }
        }
        void Recv_SetName(string name)
        {
            this.name = name;
            ChatServer.OnSetName(this);
        }
        void Recv_SendMsg(string msg)
        {
            JObject jobj = JObject.Parse(msg);
            jobj["sender"] = this.name;
            //往msg里面加入发送者信息
            ChatServer.OnSendMsg(jobj.ToString());
        }
        void Recv_GetAllUser()
        {
            var userinfo = ChatServer.GetAllUser();
            foreach (var i in userinfo)
            {
                Send_UserIn(i.Key, i.Value, i.Key == this.guid);
            }
        }
        void Recv_GetLastestMagID()
        {
            var id = ChatServer.lastestMsgID;
            Send_LastestMsgID(id);
        }
        void Recv_GetMsg(UInt64 msgid)
        {
            var msg = ChatServer.GetMsg(msgid);
            Send_Msg(msgid, msg);
        }
        public void Send_UserIn(Guid guid, string username, bool self)
        {
            JObject jobj = new JObject();
            jobj["cmd"] = "userin";
            jobj["guid"] = guid.ToString();
            jobj["name"] = username;
            jobj["self"] = self;
            SendTxtAsync(jobj.ToString());
        }
        public void Send_UserExit(Guid guid)
        {
            JObject jobj = new JObject();
            jobj["cmd"] = "userexit";
            jobj["guid"] = guid.ToString();
            SendTxtAsync(jobj.ToString());
        }
        public void Send_LastestMsgID(UInt64 msgid)
        {
            JObject jobj = new JObject();
            jobj["cmd"] = "msgid";
            jobj["msgid"] = msgid.ToString();
            SendTxtAsync(jobj.ToString());
        }
        public void Send_Msg(UInt64 msgid, string msg)
        {
            JObject jobj = JObject.Parse(msg);
            jobj["cmd"] = "msg";
            jobj["msgid"] = msgid.ToString();
            jobj["msg"] = msg;
            SendTxtAsync(jobj.ToString());
        }
        async Task SendTxtAsync(string txt)
        {
            byte[] msg = System.Text.Encoding.UTF8.GetBytes(txt);
            await this.websocket.SendAsync(
            msg,//发包 类型为ArraySegment<byte> byte[]的一部分，直接传byte[]会隐式自动转换
            System.Net.WebSockets.WebSocketMessageType.Text,//包类型 二进制还是Text
                                                            //大部分情况无所谓，但js要注意，ws in js，文本会直接收到字符串，不能发非字符，二进制会收到一个blob对象
            true,//是否是结束包，写逻辑没必要专门把一个包分几次发，基本上会一直取true
            CancellationToken.None//Task取消机制，略
            );
        }
    }
}
